﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Nvigorate.Common;

namespace Nvigorate.Generation
{
    
    public enum Constructs
    {
        codeClass,
        codeVariable,
        codeField,
        codeProperty,
        codeFunction,
        codeAttribute,
        codeLine,
        codeInitializer,
        codeArgument,
        codeArgumentDefinition
    }

    /// <summary>
    /// TO DO:
    /// 1. Enforce simple code rules (no duplicate names)
    /// 2. Provide correct indentation support
    /// </summary>
    public class CodeEntity
    {
        #region Fields

        protected string _accessLevel = "public";
        protected string _declarationTemplate = "";
        protected string _keyword = "";
        protected string _type = "";
        protected string _value = "";
        protected string _name = "";
        protected string _namespace = "";
        protected List<CodeEntity> contents = new List<CodeEntity>();
        protected List<CodeEntity> arguments = new List<CodeEntity>();
        protected List<CodeEntity> argumentDefinitions = new List<CodeEntity>();
        protected List<CodeEntity> attributes = new List<CodeEntity>();
        protected List<CodeEntity> initializers = new List<CodeEntity>();
        protected List<CodeEntity> properties = new List<CodeEntity>();
        protected List<CodeEntity> variables = new List<CodeEntity>();
        protected List<CodeEntity> fields = new List<CodeEntity>();

        protected bool _hasArguments = false;
        protected bool _hasGetter = false;
        protected bool _hasSeter = false;
        protected Constructs _construct = Constructs.codeLine;

        #endregion
        
        #region Properties

        public string AccessLevel
        {
            get { return _accessLevel; }
            set { _accessLevel = value; }
        }

        public string ClassNamespace
        {
            get { return _namespace; }
            set { _namespace = value;  }
        }

        public Constructs Construct
        {
            get { return _construct; }
            set { _construct = value; }
        }

        public string DeclarationTemplate
        {
            get { return _declarationTemplate; }
            set { _declarationTemplate = value; }
        }

        public bool HasGetter
        {
            get { return _hasGetter; }
            set { _hasGetter = value; }
        }

        public bool HasSeter
        {
            get { return _hasSeter; }
            set { _hasSeter = value; }
        }

        public string Keyword
        {
            get { return _keyword; }
            set { _keyword = value; }
        }

        public string Type
        {
            get { return _type; }
            set { _type = value; }
        }

        public string Value
        {
            get { return _value; }
            set { _value = value; }
        }

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public List<CodeEntity> Contents
        {
            get { return contents; }
            set { contents = value; }
        }

        public List<CodeEntity> Arguments
        {
            get { return arguments; }
            set { arguments = value; }
        }

        public List<CodeEntity> Attributes
        {
            get { return attributes; }
            set { attributes = value; }
        }

        public List<CodeEntity> Initializers
        {
            get { return initializers; }
            set { initializers = value; }
        }

        public List<CodeEntity> Properties
        {
            get { return properties; }
            set { properties = value; }
        }

        public List<CodeEntity> Variables
        {
            get { return variables; }
            set { variables = value; }
        }

        public List<CodeEntity> Fields
        {
            get { return fields; }
            set { fields = value; }
        }

        public bool HasArguments
        {
            get { return _hasArguments; }
            set { _hasArguments = value; }
        }

        #endregion


        public static CodeEntity CreateClass(string accessLevel, string name, string classNameSpace)
        {
            CodeEntity entity = new CodeEntity()
                                    {
                                        ClassNamespace = classNameSpace,
                                        Name = name,
                                        AccessLevel = accessLevel,
                                        Construct = Constructs.codeClass
                                    };

            entity.DeclarationTemplate =
                @"
using System;
using Nvigorate.Aspect;
using Nvigorate.Relational.Binding;

namespace {0}
$($
    {1}
    {2} partial class {3}
    $($
        #region Fields
        {4}
        #endregion   

        #region Properties
        {5}
        #endregion
    $)$
$)$";

            return entity;
        }

        public static CodeEntity CreateAttribute(string name, List<CodeEntity> initializers, params object[] arguments)
        {
            CodeEntity entity = new CodeEntity()
                                    {
                                        Name = name,
                                        Construct = Constructs.codeAttribute
                                    };

            entity.initializers.AddRange(initializers);

            foreach (object argument in arguments)
            {
                entity.arguments.Add(CodeEntity.CreateArgument(argument.ToString()));
            }

            entity.DeclarationTemplate = @"[{0}({1}{2})]";

            return entity;
        }

        public static CodeEntity CreateArgument(string value)
        {
            CodeEntity entity = new CodeEntity()
                                    {
                                        Value = value,
                                        Construct = Constructs.codeArgument
                                    };
            return entity;
        }
    
        public static CodeEntity CreateField(string accessLevel, string type, string name, string defaultValue)
        {
            CodeEntity entity = new CodeEntity()
                                    {
                                        AccessLevel = accessLevel,
                                        Type = type,
                                        Name = name,
                                        Value = defaultValue,
                                        Construct = Constructs.codeField
                                    };
            entity.DeclarationTemplate = "{0} {1} {2} = {3};";
            return entity;
        }

        public static CodeEntity CreateProperty(string accessLevel, string type, string name, string fieldName, bool hasGetter, bool hasSetter)
        {
            CodeEntity entity = new CodeEntity()
                                    {
                                        Name = name,
                                        Type = type,
                                        Value = fieldName,
                                        AccessLevel = accessLevel,
                                        HasGetter = hasGetter,
                                        HasSeter = hasSetter,
                                        Construct = Constructs.codeProperty
                                    };
            entity.DeclarationTemplate =
                @"
        {0}
        {1} {2} {3}
        $($
            get $($ return {4}; $)$
            set $($ {4} = value; $)$
        $)$
";
            return entity;
        }

        public static CodeEntity CreateInitializer(string name, string value)
        {
            CodeEntity entity = new CodeEntity()
                                    {
                                        Name = name,
                                        Value = value,
                                        Construct = Constructs.codeInitializer
                                    };
            entity.DeclarationTemplate = @"{0} = {1}";
            return entity;
        }

        public override string ToString()
        {
            string block = "";
            switch(_construct)
            {
                case Constructs.codeClass:
                    block = string.Format(DeclarationTemplate, ClassNamespace, GetAttributes(), AccessLevel, Name, GetFields(), GetProperties());
                    break;
                case Constructs.codeAttribute:
                    string init = GetInitializers();
                    block = string.Format(DeclarationTemplate, Name, GetArguments(), init == "" ? "" : ", " + init);
                    break;
                case Constructs.codeArgument:
                    block = Value;
                    break;
                case Constructs.codeField:
                    block = string.Format(DeclarationTemplate, AccessLevel, Type, Name, Value);
                    break;
                case Constructs.codeProperty:
                    block = string.Format(DeclarationTemplate, GetAttributes(), AccessLevel, Type, Name, Value);
                    break;
            }
            return Regex.Replace(block, "[$]([(]|[)])[$]", new MatchEvaluator( m => m.Value.Contains("(") ? "{" : "}"));
        }

        private string GetAttributes()
        {
            DelimitedBuilder builder = new DelimitedBuilder("\r\n\t\t");
            
            attributes.ForEach(a => builder.Append(a.ToString()));

            return builder.ToString();
        }

        private string GetFields()
        {
            DelimitedBuilder builder = new DelimitedBuilder("\r\n\t\t");

            fields.ForEach(f => builder.Append(f.ToString()));

            return builder.ToString();
        }

        private string GetProperties()
        {
            DelimitedBuilder builder = new DelimitedBuilder("\t\t");

            properties.ForEach(p => builder.Append(p.ToString()));

            return builder.ToString();
        }
    
        private string GetArguments()
        {
            DelimitedBuilder builder = new DelimitedBuilder(", ");
            arguments.ForEach(a => builder.Append(a.ToString()));
            return builder.ToString();
        }

        private string GetInitializers()
        {
            DelimitedBuilder builder = new DelimitedBuilder(", ");
            initializers.ForEach(i => builder.Append(i.ToString()));
            return (Construct == Constructs.codeVariable)
                       ? string.Concat("{", builder.ToString(), "}") : builder.ToString();
        }
    
    }


}
